D - AtCoder Janken 3题解

D - AtCoder Janken 3题解

题意:

高桥和青 木要玩石头剪刀布,给你一个长度为 nn 的字符串 ssss 表示青木在第 ii 局游戏中的动作(R表示石头,P表示布,S表示剪刀。)。

高桥不可以在任何一局中输给青木(即:高桥和青木只可以平局或高桥赢青木),且高桥第 ii 局出的和第 i1i-1 局出的不能一样,现在,问你高桥可以胜几局?

解题思路

很明显是一道 DP\textrm{DP} 题:

定义状态:

fi,jf_{i,j} 表示第 ii 局出 jj 时最多可以获胜的局数(j=1j = 1时表示出石头,j=2j = 2时表示出布,j=3j = 3时表示出剪刀)。

初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
if (s[0] == 'R') {
f[0][1] = 0;//两人一起出石头,不计分
f[0][2] = 1;//高桥出布,高桥加一分
//注意:高桥不可以输给青木,所以f[0][3]不可以初始化为-1
}
if (s[0] == 'P') {
f[0][2] = 0;//同理
f[0][3] = 1;
}
if (s[0] == 'S') {
f[0][1] = 1;
f[0][3] = 0;
}

转移方程:

si=s_i = R 时:

fi,1=max(fi1,2,fi1,3)fi,2=max(fi1,1,fi1,3)+1f_{i,1} = \max(f_{i-1,2}, f_{i-1,3})\\ f_{i,2} = \max(f_{i-1,1}, f_{i-1,3}) + 1

(注意:fi,1f_{i,1} 是被 fi1,2f_{i-1,2}fi1,3f_{i-1,3}转移过来的,因为高桥第 ii 局出的和第 i1i-1 局出的不能一样,
高桥不可以中输给青木,所以 jj 不可以取 33 ,即:不可以出剪刀)。
si=s_i = P 时:

fi,2=max(fi1,1,fi1,3)fi,3=max(fi1,2,fi1,3)+1f_{i,2} = \max(f_{i-1,1}, f_{i-1,3})\\ f_{i,3} = \max(f_{i-1,2}, f_{i-1,3}) + 1

si=s_i = S 时:

fi,1=max(fi1,2,fi1,3)+1fi,3=max(fi1,1,fi1,2)f_{i,1} = \max(f_{i-1,2}, f_{i-1,3}) + 1\\ f_{i,3} = \max(f_{i-1,1}, f_{i-1,2})

答案就取 max(fn1,1,fn1,2,fn1,3)\max(f_{n - 1,1},f_{n - 1,2},f_{n - 1,3}),(我的字符串 ss 下标从 00 开始存)。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int n, ans, f[N][10];
string s;
main() {
cin >> n >> s;
if (s[0] == 'R') {
f[0][1] = 0;
f[0][2] = 1;
}
if (s[0] == 'P') {
f[0][2] = 0;
f[0][3] = 1;
}
if (s[0] == 'S') {
f[0][1] = 1;
f[0][3] = 0;
}
for (int i = 1; i < n; i++) {
if (s[i] == 'R') {
f[i][1] = max(f[i - 1][2], f[i - 1][3]);
f[i][2] = max(f[i - 1][1], f[i - 1][3]) + 1;
}
if (s[i] == 'P') {
f[i][2] = max(f[i - 1][1], f[i - 1][3]);
f[i][3] = max(f[i - 1][1], f[i - 1][2]) + 1;
}
if (s[i] == 'S') {
f[i][1] = max(f[i - 1][2], f[i - 1][3]) + 1;
f[i][3] = max(f[i - 1][1], f[i - 1][2]);
}
}
cout << max({f[n - 1][1], f[n - 1][2], f[n - 1][3]});
return 0;
}